#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "driver/gpio.h"

#include "led.h"

#define LED_PIN 0
#define LED_ON_LEVEL 0

static QueueHandle_t led_eventqueue = NULL;
static uint8_t led_sta = 0;

void led_queuesend_state(uint8_t cmd)
{
    if (led_eventqueue == NULL) {
        return;
    }

    xQueueSend(led_eventqueue, &cmd, pdMS_TO_TICKS(500));
}

void led_queuesend_state_fromISR(uint8_t cmd)
{
    if (led_eventqueue == NULL) {
        return;
    }

    xQueueSendFromISR(led_eventqueue, &cmd, NULL);
}

void led_on(void)
{
    gpio_set_level(LED_PIN, LED_ON_LEVEL);
    led_sta = 1;
}

void led_off(void)
{
    gpio_set_level(LED_PIN, !LED_ON_LEVEL);
    led_sta = 0;
}

void led_flash(void)
{
    uint8_t delay = 100;

    led_on();
    vTaskDelay(delay / portTICK_PERIOD_MS);
    led_off();
    vTaskDelay(delay / portTICK_PERIOD_MS);

    led_on();
    vTaskDelay(delay / portTICK_PERIOD_MS);
    led_off();
    vTaskDelay(delay / portTICK_PERIOD_MS);
}

void led_toggle(void)
{
    led_sta = !led_sta;
    gpio_set_level(LED_PIN, led_sta);
}

static void led_queue_task(void* arg)
{
    uint8_t event;

    while (1) {
        if (xQueueReceive(led_eventqueue, &event, pdMS_TO_TICKS(1000))) {
            switch (event) {
                case LED_OFF:
                    led_off();
                    break;
                case LED_ON:
                    led_on();
                    break;
                case LED_FLASH:
                    led_flash();
                    break;
                case LED_BLINK:
                    led_toggle();
                    break;
                default:
                    ESP_LOGE("LED", "Unknown event %d\n", event);
                    break;
            }
        } else {
            if (event == LED_BLINK) {
                led_toggle();
            }
        }
    }
}

void led_gpio_init(int8_t pin)
{
    gpio_reset_pin(pin);

    /* Set the GPIO as a push/pull output */
    gpio_set_direction(pin, GPIO_MODE_OUTPUT);

    gpio_set_level(pin, LED_ON_LEVEL);
}

int led_init(void)
{
    led_gpio_init(LED_PIN);

    led_eventqueue = xQueueCreate(10, sizeof(uint8_t));
    if (led_eventqueue == NULL) {
        ESP_LOGE("LED", "Failed to create queue");
        return -1;
    }

    xTaskCreate(led_queue_task, "led_queue_task", 2048, NULL, 10, NULL);

    return 0;
}

